package com.yungu.swift.order.helper;

import com.alibaba.dubbo.config.annotation.Reference;
import com.google.common.collect.Lists;
import com.yungu.swift.assets.driver.service.DriverService;
import com.yungu.swift.base.ResponseData;
import com.yungu.swift.constants.OrderConstant;
import com.yungu.swift.lbs.model.DriverPoint;
import com.yungu.swift.lbs.model.vo.RegeoVo;
import com.yungu.swift.lbs.service.DistanceService;
import com.yungu.swift.lbs.service.LocationService;
import com.yungu.swift.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.yungu.swift.order.model.enums.OrderErrorEnum.GRAB_ERROR_1001;
import static com.yungu.swift.order.model.enums.OrderErrorEnum.GRAB_ERROR_1003;
import static com.yungu.swift.order.model.enums.OrderErrorEnum.GRAB_ERROR_1100;

/**
 * @author : cuixiuyin
 * @date : 2019/6/5
 */
@Slf4j
@Component
public class OrderAssignHelper {

    private static DriverService driverService;
    private static LocationService locationService;
    private static DistanceService distanceService;


    public static boolean joinWayMatch(String appid, Double lng, Double lat, Integer type, String includeAreas, String cityName,
                                       Double centerLng, Double centerLat) {
        /*获取司机当前位置所处行政区域（【逆】地理位置编码）*/
        String city = null;
        String district = null;
        ResponseData<RegeoVo> regeo = locationService.regeo(lng, lat);
        if (regeo.isSuccess() && regeo.getData() != null) {
            RegeoVo.AddressComponent addressComponent = regeo.getData().getAddressComponent();
            if (addressComponent != null) {
                city = addressComponent.getCity();
                district = addressComponent.getDistrict();
            }
        }
        //匹配区级（按行政区域匹配：先匹配区县，如果全市则按照市级匹配）
        if (type == OrderConstant.POINT_SITE_TYPE_AREA) {
            if (StringUtils.isNotEmpty(district) && (levenshtein(includeAreas, district) >= 0.66f || includeAreas.equals("全部"))) {
                return true;
            }
            if (StringUtils.isNotEmpty(city) && cityName.equals(city)) {
                return true;
            }
        }
        //匹配固定点（距离中心点500米）
        if (type == OrderConstant.POINT_SITE_TYPE_POINT) {
            ResponseData<Double> getDistance = distanceService.getDistance(lng, lat, centerLng, centerLat);
            if (getDistance.isSuccess() && getDistance.getData() != null && getDistance.getData() <= 500d) {
                return true;
            }
        }
        return false;
    }

    /**
     * 比较两个字符串的相似度，并返回相似率。
     *
     * @param str1
     * @param str2
     * @return
     */
    private static float levenshtein(String str1, String str2) {
        if (str1.contains(str2)) {
            return 1.0f;
        }
        //计算两个字符串的长度。
        int len1 = str1.length();
        int len2 = str2.length();
        //建立上面说的数组，比字符长度大一个空间
        int[][] dif = new int[len1 + 1][len2 + 1];
        //赋初值，步骤B。
        for (int a = 0; a <= len1; a++) {
            dif[a][0] = a;
        }
        for (int a = 0; a <= len2; a++) {
            dif[0][a] = a;
        }
        //计算两个字符是否一样，计算左上的值
        int temp;
        for (int i = 1; i <= len1; i++) {
            for (int j = 1; j <= len2; j++) {
                if (str1.charAt(i - 1) == str2.charAt(j - 1)) {
                    temp = 0;
                } else {
                    temp = 1;
                }
                //取三个值中最小的
                dif[i][j] = min(dif[i - 1][j - 1] + temp, dif[i][j - 1] + 1, dif[i - 1][j] + 1);
            }
        }
        //计算相似度
        return 1 - (float) dif[len1][len2] / Math.max(len1, len2);
    }

    /**
     * 求最小值
     *
     * @return
     */
    private static int min(int... is) {
        int min = Integer.MAX_VALUE;
        for (int i : is) {
            if (min > i) {
                min = i;
            }
        }
        return min;
    }

    /**
     * 推单：获取可抢单单司机
     */
    public static List<String> filterGrabDriver(String appid, Integer typeModule, Double originLng, Double originLat,
                                                String carModelValuationUuid, int distance, int maxNum, Integer seatNum,
                                                String originCityUuid, boolean isStationOrder) {
        List<String> driverIds = getNumDriverByDistace(appid, typeModule, originLng, originLat, distance, maxNum);
        log.info("---根据距离：{}， 得到的司机列表： dricerIds----{}", distance, driverIds);
        if (CollectionUtils.isNotEmpty(driverIds)) {
            List<String> ids = driverService.filterGrabDriver(carModelValuationUuid, driverIds, false, seatNum, originCityUuid, isStationOrder).getData();
            if (CollectionUtils.isEmpty(ids)) {
                log.info("---根据业务，得到的司机列表 ids：{}", ids);
                log.warn("无匹推单单司机，参数【driverIds:{}】【carModelValuationUuid:{}】【seatNum:{}】【originCityUuid:{}】",
                        driverIds, carModelValuationUuid, seatNum, originCityUuid);
            }
            log.info("---根据业务，得到的可抢单司机列表 ids：{}", ids);
            return ids;
        }
        return driverIds;
    }

    /**
     * 获取传入距离内的司机个数
     *
     * @param appid      租户标识
     * @param typeModule 业务类型
     * @param originLng  起点
     * @param originLat  终点
     * @param distance   距离
     * @param maxNum     最大数量
     * @return
     */
    private static List<String> getNumDriverByDistace(String appid, Integer typeModule, Double originLng, Double originLat,
                                                      int distance, int maxNum) {
        List<DriverPoint> list = locationService.geoNear(typeModule, originLng, originLat, appid, distance, maxNum).getData();
        if (CollectionUtils.isNotEmpty(list)) {
            return list.stream().map(driverPoint -> driverPoint.getDriverUuid()).collect(Collectors.toList());
        }
        return Lists.newArrayList();
    }

    /**
     * 获取同应用的同城司机,并且根据车型来过滤
     *
     * @param appid
     * @param typeModule
     * @param originCityUuid
     * @param carModelValuationUuid
     * @param departTime
     * @param remindType
     * @param seatNum
     * @return
     */
    public static List<String> getTheSameCityDriverByCarType(String appid, Integer typeModule, String originCityUuid,
                                                             String carModelValuationUuid, Date departTime, Integer remindType,
                                                             Integer seatNum, boolean isStationOrder) {
        List<String> driverIds = driverService.getTheSameCityDriver(appid, typeModule, originCityUuid).getData();
        //filterGrabDriverReservation 方法对于预约单要改成前后一小时内无法接单，而不是直接有未完成订单就不能接
        //还需要传入订单出发时间，方便判断
        if (CollectionUtils.isNotEmpty(driverIds)) {
            log.info("----取得同城的司机ID:{}----", driverIds);
            List<String> ids = driverService.filterGrabDriverReservation(carModelValuationUuid, driverIds, departTime,
                    remindType, seatNum, originCityUuid, isStationOrder).getData();
            if (CollectionUtils.isEmpty(ids)) {
                log.info("----无匹配预约司机ID" + "参数：【carModelValuationUuid:" + carModelValuationUuid + "】" +
                        "【driverIds:" + driverIds + "】【departTime:" + departTime + "】" +
                        "【remindType:" + remindType + "】【seatNum:" + seatNum + "】------");
            }
            return ids;
        }
        log.info("----获取不到同城的司机ID，参数：【appid:" + appid + "】【type:" + typeModule + "】【cityUuid:" + originCityUuid + "】----");
        return driverIds;
    }


    /**
     * 抢单状态校验
     */
    public static ResponseData<Void> grabOrderStatusCheck(Integer mainStatus, Integer subStatus) {
        if (OrderConstant.ORDER_MAIN_STATUS_DOING.equals(mainStatus)
                || OrderConstant.ORDER_MAIN_STATUS_DONE.equals(mainStatus)
                || OrderConstant.ORDER_MAIN_STATUS_PAYED.equals(mainStatus)) {
            //主状态 2 进行中， 3 司机已送达、待支付， 4 支付完成、订单结束
            return ResponseData.buildErrorResponse(GRAB_ERROR_1001.getCode(), GRAB_ERROR_1001.getMsg());
        }
        if (OrderConstant.ORDER_MAIN_STATUS_CANCEL.equals(mainStatus)) {
            return ResponseData.buildErrorResponse(GRAB_ERROR_1003.getCode(), GRAB_ERROR_1003.getMsg());
        }
        if (!OrderConstant.ORDER_MAIN_STATUS_INITIAL.equals(mainStatus) || !OrderConstant.ORDER_STATUS_WAIT.equals(subStatus)) {
            return ResponseData.buildErrorResponse(GRAB_ERROR_1100.getCode(), GRAB_ERROR_1100.getMsg());
        }
        return ResponseData.buildSuccessResponse(null);
    }

    @Reference
    public void setDriverService(DriverService driverService) {
        OrderAssignHelper.driverService = driverService;
    }

    @Reference
    public void setLocationService(LocationService locationService) {
        OrderAssignHelper.locationService = locationService;
    }

    @Reference
    public void setDistanceService(DistanceService distanceService) {
        OrderAssignHelper.distanceService = distanceService;
    }
}
